﻿using System;
using System.Collections.Generic;
#if DEBUG
using System.Diagnostics;
#endif // #if DEBUG
using System.Linq;
using System.Text;

using FRL.DebtChased.Types;
using FRL.DebtChased.WebServices;

namespace FRL.DebtChased.Repository
{

	/// <summary>
	/// Extensions to the AccountPlan model
	/// </summary>
	public partial class AccountPlan
	{

		/////////////////////////////////////////////////////////////
		// Attributes

		public Definitions.eInstalmentPaymentMethod PaymentMethod
		{
			get { return (Definitions.eInstalmentPaymentMethod)this._fdPaymentMethod; }
			set { this.fdPaymentMethod = (int)value; }
		}

		/////////////////////////////////////////////////////////////
		// Static Operations

		public static string SetActiveAccountPlan(DebtChasedEntities ent, AccountPlanTransport accountPlanTransport, int? iUserId)
		{
#if DEBUG
			Debug.Assert(accountPlanTransport.AccountId != 0);
			Debug.Assert(accountPlanTransport.AccountInvoiceStatusId != 0);
#endif // #if DEBUG

			// Declare return variable
			string strError = null;

			// Declaration of local variables

			try
			{

				// Get the current plan
				AccountPlan existingAccountPlan = (from ap in ent.AccountPlans
										   where (ap.AccountId == accountPlanTransport.AccountId) && (ap.IsActive)
										   select ap).FirstOrDefault();
				if (existingAccountPlan != null)
					strError = "An active instalment plan exists for this account.";

				// If that worked...
				if (strError == null)
				{

					// Compose plan description
					string strDescription = TransportSupportForWebsite.ComposePlanDescription(accountPlanTransport);

					// Create a new entity object
					AccountPlan accountPlan = AccountPlan.CreateAccountPlan(
						0,
						accountPlanTransport.AccountId,
						strDescription,
						accountPlanTransport.ConfirmedAt,
						true,
						0,
						(int)Frequency.ePeriod.Weekly,
						(int)Definitions.eInstalmentPaymentMethod.Chaps
					);
					ent.AccountPlans.AddObject(accountPlan);

					// Add payments to the plan
					foreach (AccountPlanPaymentTransport payment in accountPlanTransport.Payments)
					{

						// Add payment to entity object
						AccountPlanPayment newPayment = AccountPlanPayment.CreateAccountPlanPayment(
							0, 
							0, 
							payment.ExpectedAt, 
							payment.Amount, 
							payment.Currency
						);
						accountPlan.AccountPlanPayments.Add(newPayment);

					}

					// Change the status of the invoices not "in-query"
					var accountInvoices = from ai in ent.AccountInvoices
										  where (ai.AccountId == accountPlanTransport.AccountId) && (!ai.AccountInvoiceStatus.IsInQuery)
										  select ai;
					foreach (AccountInvoice inv in accountInvoices)
						inv.AccountInvoiceStatusId = accountPlanTransport.AccountInvoiceStatusId;

					// Create a new account note
					AccountNote accountNote = AccountNote.Append(
						ent,
						accountPlanTransport.AccountId,
						string.Format("New instalment plan confirmed.  {0}", strDescription ),
						null,
						iUserId,
						AccountNote.eFlags.NotSet,
						true,
						false
					);
					accountPlan.AccountNote = accountNote;
					
					// Save changes
					ent.SaveChanges();

				}

			}
			catch (Exception x)
			{

				// Process exception
				strError = ErrorProcessing.HandleException(x, "save account plan", iUserId);

			}

			// Return variable
			return strError;

		}

		public static string KillActiveAccountPlan(DebtChasedEntities ent, int iAccountPlanId, string strReason, bool bAlertManager, int? iUserId)
		{
#if DEBUG
			Debug.Assert(iAccountPlanId != 0);
#endif // #if DEBUG

			// Declare return variable
			string strError = null;

			try
			{

				// Get the current plan
				AccountPlan accountPlan = (from ap in ent.AccountPlans
										   where (ap.AccountPlanId == iAccountPlanId) && (ap.IsActive)
										   select ap).FirstOrDefault();
				if (accountPlan == null)
					strError = "There is no active plan for this account.";

				// If that worked...
				if (strError == null)
				{

					// Kill the account-plan
					accountPlan.IsActive = false;

					// Change the status of the invoices not "in-query"
					var accountInvoices = from ai in ent.AccountInvoices
										  where (
											(ai.AccountId == accountPlan.AccountId) 
											&& 
											(
												(ai.AccountInvoiceStatusId == null)
												||
												(!ai.AccountInvoiceStatus.IsInQuery)
											)
										  )
										  select ai;
					foreach (AccountInvoice inv in accountInvoices)
						inv.AccountInvoiceStatusId = null;

					// Add a note
					StringBuilder sb = new StringBuilder();
					sb.AppendFormat("Installment plan cancelled. {0}", strReason);

					// Create a new account note
					AccountNote.Append(
						ent,
						accountPlan.AccountId,
						sb.ToString(),
						null,
						iUserId,
						AccountNote.eFlags.NotSet,
						true,
						false
					);

					// Save changes
					ent.SaveChanges();

				}

			}
			catch (Exception x)
			{

				// Process exception
				strError = ErrorProcessing.HandleException(x, "kill account plan", iUserId);

			}

			// Return variable
			return strError;

		}

		public static AccountPlanTransport GetActiveTransport( DebtChasedEntities ent, int iAccountId )
		{

			// Declare return variable
			AccountPlanTransport Ftmp = null;

			// Load plan, if there is one
			var plan = (from ap in ent.AccountPlans.Include("AccountPlanPayments")
					   where (ap.AccountId == iAccountId) && (ap.IsActive)
					   select ap).FirstOrDefault();
			if (plan != null)
			{

				// Load the transport object
				Ftmp = new AccountPlanTransport() { 
					AccountPlanId = plan.AccountPlanId,
					AccountId = plan.AccountId,
					AccountNoteId  = plan.AccountNoteId,
					ConfirmedAt = plan.ConfirmedAt,
					Description = plan.Description,
					IsActive = plan.IsActive,
					Period = plan.fdPeriod,
					PaymentMethod = plan.fdPaymentMethod							 
				};
				Ftmp.Payments = new List<AccountPlanPaymentTransport>();

				// Load the payments
				foreach (AccountPlanPayment payment in plan.AccountPlanPayments)
				{

					// Add payment transport
					Ftmp.Payments.Add(new AccountPlanPaymentTransport()
					{ 
						AccountPlanPaymentId = payment.AccountPlanPaymentId,
						Amount = payment.Amount,
						Currency = payment.Currency,
						ExpectedAt = payment.ExpectedAt,
						TransactionId = payment.TransactionId
					});

				} // foreach-loop

			}

			// Return variable
			return Ftmp;

		}

		public static string SetAgentTransport( DebtChasedEntities ent, AgentTransport agentTransport, int iUserId )
		{
#if DEBUG
			Debug.Assert(agentTransport.Contacts != null);
			Debug.Assert(agentTransport.BankAccounts != null);
#endif // #if DEBUG

			// Declare return variable
			string strError = null;

			// Compose coverage string
			StringBuilder sbCoverage = new StringBuilder();
			foreach (string strCode in agentTransport.CoverageCountryCodes)
			{
				if (sbCoverage.Length > 0)
					sbCoverage.Append(",");
				sbCoverage.Append(strCode);
			}

			try
			{

				// Get all agent objects
				Agent currentAgent = null;
				if (agentTransport.AgentId == 0)
				{

					// Create a new agent
					currentAgent = new Agent();
					ent.Agents.AddObject(currentAgent);

				}
				else
				{

					// Load the agent details including bank acccounts
					currentAgent = (from a in ent.Agents.Include("AgentBankAccounts").Include("AgentContacts")
									where (a.AgentId == agentTransport.AgentId)
									select a).First();
#if DEBUG
					Debug.Assert(currentAgent != null);
#endif // #if DEBUG

				}

				// Track user activity
				UserAction.Session session = new UserAction.Session(iUserId, currentAgent, agentTransport.Name);

				// Process bank accounts
				AgentBankAccount.InterCursorSetFromTransport(ent, session, currentAgent, agentTransport, iUserId);

				// Process contacts
				Dictionary<int, AgentContact> contactMap = new Dictionary<int, AgentContact>();
				AgentContact.InterCursorSetFromTransport(ent, session, currentAgent, agentTransport, contactMap, iUserId);

				// Update the agent
				if (currentAgent.fdAgentType != agentTransport.AgentType)
					currentAgent.fdAgentType = agentTransport.AgentType;

				if(currentAgent.CountryCodes != sbCoverage.ToString())
					currentAgent.CountryCodes = sbCoverage.ToString();

				if(currentAgent.Name != agentTransport.Name)
					currentAgent.Name = agentTransport.Name;

				if(currentAgent.Notes != agentTransport.Notes)
					currentAgent.Notes = agentTransport.Notes;

				if(currentAgent.Website != agentTransport.Website)
					currentAgent.Website = agentTransport.Website;

				if(currentAgent.UseAccountCommissionRate != agentTransport.UseAccountCommissionRate)
					currentAgent.UseAccountCommissionRate = agentTransport.UseAccountCommissionRate;

				if (currentAgent.SpecificCommissionRatePerc != agentTransport.SpecificCommissionRatePerc)
					currentAgent.SpecificCommissionRatePerc = agentTransport.SpecificCommissionRatePerc;

				// If session has changes...
				if (session.HasChanges())
				{

					// Save changes
					ent.SaveChanges();

					// Update agent
					if (agentTransport.AgentId == 0)
						agentTransport.AgentId = currentAgent.AgentId;
#if DEBUG
					else
						Debug.Assert(agentTransport.AgentId == currentAgent.AgentId);
#endif // #if DEBUG

					// Add the session objects
					session.SaveChanges(ent);

				}

			}
			catch (Exception x)
			{

				// Handle exception
				strError = ErrorProcessing.HandleException(x, "save agent", iUserId);

			}

			// Return variable
			return strError;

		}

		public static List<AgentSummaryTransport> TransportSearch(DebtChasedEntities ent, string strCountryCode, int iUserId)
		{

			// Declare return variable
			List<AgentSummaryTransport> Ftmp = new List<AgentSummaryTransport>();

			try
			{

				// Format search string
				string strSearch;
				if (strCountryCode.Contains("%"))
					strSearch = strCountryCode;
				else
					strSearch = string.Format("%{0}%", strCountryCode);

				// Load from SP function
				var results = ent.SearchAgentsProc(strSearch);
				foreach (var result in results)
				{

					// Load the return variable
					AgentSummaryTransport ast = new AgentSummaryTransport();
					Ftmp.Add(ast);
					ast.AgentId = result.fdAgentId;
					ast.AgentType = (result.fdAgentType == (int)Definitions.eAgentType.Agent) ? "Agent" : "Solicitor";
					ast.CoverageCountryCodes = result.fdCountryCodes;
					ast.Contacts = result.fdContacts;
					ast.Name = result.fdName;

				}

			}
			catch (Exception x)
			{

				// Process exception
				ErrorProcessing.HandleException(x, "search agents", iUserId);

				// Nullify list
				Ftmp = null;

			}

			// Return variable
			return Ftmp;

		}

	}

}
